package cn.xydpeng.springframework.context.event;

import cn.xydpeng.springframework.beans.BeansException;
import cn.xydpeng.springframework.beans.factory.BeanClassLoaderAware;
import cn.xydpeng.springframework.beans.factory.BeanFactory;
import cn.xydpeng.springframework.beans.factory.BeanFactoryAware;
import cn.xydpeng.springframework.beans.factory.config.ConfigurableBeanFactory;
import cn.xydpeng.springframework.context.ApplicationEvent;
import cn.xydpeng.springframework.context.ApplicationListener;
import cn.xydpeng.springframework.core.ResolvableType;
import cn.xydpeng.springframework.lang.Nullable;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author xydpeng
 * @version v1.0
 * @date: 2022/10/21
 * @description:
 */
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    //需要注册、管理监听器（观察者）

    private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();

    private class DefaultListenerRetriever {
        public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
    }


    @Nullable
    private ClassLoader beanClassLoader;
    @Nullable
    private ConfigurableBeanFactory beanFactory;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableBeanFactory) beanFactory;
        if (this.beanClassLoader == null) {
            this.beanClassLoader = this.beanFactory.getBeanClassLoader();
        }
    }

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.defaultRetriever) {
            this.defaultRetriever.applicationListeners.add(listener);
        }
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.defaultRetriever) {
            this.defaultRetriever.applicationListeners.remove(listener);
        }
    }

    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
        Set<ApplicationListener<?>> listeners = new HashSet<>(this.defaultRetriever.applicationListeners);

        //TODO 其他处理
        ArrayList<ApplicationListener> list = new ArrayList<>();
        for (ApplicationListener<?> listener : listeners) {
            if (supportsEvent(listener, eventType)) {
                list.add(listener);
            }
        }
        return list;
    }

    protected boolean supportsEvent(ApplicationListener listener, ResolvableType eventType) {
        //TODO spring源码中加入了适配器模式GenericApplicationListenerAdapter，属性declaredEventType 缓存了listener申明支持的的eventType

        Class<? extends ApplicationListener> listenerType = listener.getClass();
        Class<?> eventClass = eventType.getResolved();
        /*ResolvableType declaredEventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric();
        if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) {
            Class<?> targetClass = AopUtils.getTargetClass(listener);
            if (targetClass != listener.getClass()) {
                declaredEventType =  ResolvableType.forClass(targetClass).as(ApplicationListener.class).getGeneric();;
            }
        }

        return (declaredEventType == null || declaredEventType.isAssignableFrom(eventType));*/

        Type[] types = listenerType.getGenericInterfaces();
        for (Type type : types) {
            Type typeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
            if (typeArgument != null) {
                if (typeArgument.getTypeName().equals(eventClass.getName())) {
                    return true;
                }
            }
        }
        return false;

    }


}
